package com.zeus.web.converters;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.hengxinyongli.zeus.common.core.Aes;
import com.hengxinyongli.zeus.common.core.RSAUtils;
import com.zeus.web.utils.StringUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonInputMessage;
import org.springframework.lang.Nullable;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Type;
import java.security.interfaces.RSAPrivateKey;

/**
 * @author magic
 * @date 2018/11/2/002 9:07
 * @version 1.0
 * Description PostRequestDecryptConverters
 */
public class PostRequestDecryptConverters extends AbstractJackson2HttpMessageConverter {

    private static final String AES_KEY_REQUEST = "X-AES-KEY";
    private static final String RSA_CUSTOMER_PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMnhh0CNonSWynIwgqDNfxaXgDjCW+EXC3oSxbWeShMj6qqcpiALYy4dfcMm495lRjAQbuRUIMwwhHg05E2Wi7H4k3Qbs/OZR9dVFsESTwjerykxdRrlG9M3CskfrFSJz9vKLP5KBb3z5xBM3oDm5BLNK7FcvCSbp04vXxoWT7KbAgMBAAECgYA6p3Tlc4EswJbnIGMDl1ECXlgZpZaOSmTCPewFfYzmDLxkf4jgrI5PWrAsYAM6nVAMLOobGrpAuSPzzOI8DK5fLbgUWbH92/UQmdVdxr78NIKZTdnpT2pvSFobAiMMigmSpgFyenHbTfEpzyqFu7m3W2yBZJ/tfOl7a8UXEoQqeQJBAOwr8UHCb2aZu67af5fjLP4mSaFkg6JpSmE0ADIiuIhuuXAFZuMVz2hG8nuwd1ofLVCwY01oMJo1pZ0W/T77pp8CQQDa1JLJS1ijTqFAHC6ZsmPqxMkF/6KMtrhI0Afi0kTNVWa15lnyrTkEpeoavp9mntw/dUi/Ws70S6Va0MelxJ6FAkEAzzZ61gSfs3i9CFxjSMOk7VRwk+9Z43zYKtLyPQrjt6jWgOfsbt/q+4YMKOQaw/LI0pwAdKEuy464QU0zYzhu5wJAAuvxpcRew5dqKbv2l1C9asYD1ROrrOtHnJlCcbHS/1bTjzwrGUVczIYWkRLYd/tI/4PmW7+voa1c4NCGydzNLQJAQvo+9IIj3a4dGjwS4s3Z/SqVitBxihMi/qNi3GBuOoEt3JxTz64G0mlVKmICnjySl7Tt4X3CAc48FVoKcrIpFA==";
    private Logger logger = LoggerFactory.getLogger(PostRequestDecryptConverters.class);

    public PostRequestDecryptConverters(ObjectMapper objectMapper){
        super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
    }

    @Override
    public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
        if(super.canRead(type, contextClass, mediaType)){
            JavaType javaType = getJavaType(type,contextClass);
            if(Decipherable.class.isAssignableFrom(javaType.getRawClass())){
                return true;
            }
        }
        return false;
    }

    @Override
    public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {

        JavaType javaType = getJavaType(type, contextClass);
        return readJavaType(javaType, inputMessage);
    }

    private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
        try {
            if (inputMessage instanceof MappingJacksonInputMessage) {
                Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
                if (deserializationView != null) {
                    return this.objectMapper.readerWithView(deserializationView).forType(javaType).
                            readValue(inputMessage.getBody());
                }
            }
            if(Decipherable.class.isAssignableFrom(javaType.getRawClass())){
                HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
                String randomKey = request.getHeader(AES_KEY_REQUEST);
                if(StringUtils.isNotEmpty(randomKey)){
                    try {
                        logger.info("randomKey: {}",randomKey);
                        RSAPrivateKey privateKey = RSAUtils.getPrivateKey(RSA_CUSTOMER_PRIVATE_KEY);
                        String decryptAesKey = RSAUtils.privateDecrypt(randomKey,privateKey);
                        String data = IOUtils.toString(inputMessage.getBody(),"UTF-8");
                        String decryptBody = Aes.decrypt(data,decryptAesKey);
                        return this.objectMapper.readValue(decryptBody, javaType);
                    } catch (Exception e) {
                        logger.error("Decrypt error: {}",ExceptionUtils.getMessage(e));
                        throw new RuntimeException(e);
                    }
                }
            }
            return this.objectMapper.readValue(inputMessage.getBody(), javaType);
        }
        catch (InvalidDefinitionException ex) {
            throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
        }
        catch (JsonProcessingException ex) {
            throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex);
        }
    }
}
